package com.haiyou.common.spring.schedule;

import com.haiyou.common.spring.schedule.handler.TaskEventHandler;
import com.haiyou.common.thread.task.AbstractRunnableCoreTask;
import com.haiyou.common.util.date.DateUtilsV2;
import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.support.CronExpression;
import org.springframework.util.Assert;

//import java.io.Serial;
import java.io.Serializable;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.temporal.ChronoUnit;
import java.util.Date;
import java.util.concurrent.*;

/**
 * boss后台使用
 */
@Getter
@Setter
@Slf4j
public abstract class RunnableScheduledBossTask extends AbstractRunnableCoreTask
        implements Delayed, Serializable {

//    @Serial
    private static final long serialVersionUID = 4095904501211005697L;

    /**
     * Futrue
     */
    private ScheduledFuture<?> futrue;

    /**
     * cron表达式
     */
    private String cron;

    /**
     * 保存下次执行时间
     */
    private Date date;

    private String taskName;

    private TaskEventHandler taskEventHandler;

    /**
     * 校验任务是否提交到定时器
     */
    private void checksubmit() {
        Assert.notNull(futrue, "task not submit in secheduled");
    }

    /**
     * @param mayInterruptIfRunning 是否运行时中断,false不允许在线程运行时中断
     */
    public boolean cancel(boolean mayInterruptIfRunning) {
        checksubmit();
        return futrue.cancel(mayInterruptIfRunning);
    }

    public boolean isCancelled() {
        checksubmit();
        return futrue.isCancelled();
    }

    public boolean isDone() {
        checksubmit();
        return futrue.isDone();
    }

    /**
     * 等待 ScheduledFuture 执行完成
     */
    public void futrueGet() throws InterruptedException, ExecutionException {
        checksubmit();
        futrue.get();
    }

    /**
     * 等待 ScheduledFuture 执行完成
     *
     * @param timeout
     * @param unit
     */
    public void futrueGet(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException {
        checksubmit();
        futrue.get(timeout, unit);
    }

    /**
     * 更新执行时间
     */
    public void updateExecuteOnDate() {
        CronExpression cronExpression = CronExpression.parse(this.cron);
        // 获取下次执行的时间
        LocalDateTime localDateTime = cronExpression.next(LocalDateTime.now());
        // 获取系统默认时区
        ZoneId zoneId = ZoneId.systemDefault();
        // 时区的日期和时间
        ZonedDateTime zonedDateTime = localDateTime.atZone(zoneId);
        this.date = Date.from(zonedDateTime.toInstant());
    }

    /**
     * 计算执行时间间隔
     */
    private long computeInterval() {
        CronExpression cronExpression = CronExpression.parse(this.cron);
        // 获取下次执行的时间
        LocalDateTime nextExecuteTime = cronExpression.next(LocalDateTime.now());
        // 下下次执行的时间
        LocalDateTime nextNextExecuteTime = cronExpression.next(nextExecuteTime);
        return ChronoUnit.MILLIS.between(nextExecuteTime, nextNextExecuteTime);
    }

    @Override
    public long getDelay(TimeUnit unit) {
        checksubmit();
        return futrue.getDelay(unit);
    }

    @Override
    public int compareTo(Delayed o) {
        checksubmit();
        return futrue.compareTo(o);
    }

    @Override
    protected final void exec() throws Exception {
        onTaskBeforeExecute();
        // 执行时间间隔
        long ntervalTime = computeInterval();
        // 下次执行时间
        long time = this.getDate().getTime();
        // 判断间隔时间是否大于一天
        if (ntervalTime > DateUtilsV2.DAY_MILLISECOND) {
            while (ntervalTime > 0) {
                ntervalTime -= DateUtilsV2.DAY_MILLISECOND;
                time -= DateUtilsV2.DAY_MILLISECOND;
                DateUtilsV2.parseYYYYMMDD(time);
                // 执行任务
                executeOnTask(new Date(DateUtilsV2.getDayZeroMillis(time)));
            }
        } else {
            // 执行任务
            executeOnTask(new Date(DateUtilsV2.getDayZeroMillis(time - ntervalTime)));
        }
        onTaskAfterExecute();
    }

    /**
     * 每次执行前调用
     */
    protected void onTaskBeforeExecute() {
        log.info("{},任务统计开始", this.taskName);
    }

    /**
     * 每次执行后调用
     */
    protected void onTaskAfterExecute() {
        updateExecuteOnDate();
        taskEventHandler.updateCount(this.taskName);
        // TODO 记录日志
        log.info("{},任务统计完成", this.taskName);
    }

    public abstract void executeOnTask(Date date) throws Exception;

}
